package testrank.predictors;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import testrank.Conflict;
import testrank.Predictor;
import testrank.TestRankConstants;

/**
 * @author Hagai Cibulski
 * TODO consider using thisEnclosingJoinPointStaticPart
 */
public class CallCountPredictor implements Predictor {

	private static final long serialVersionUID = 1L;

	/**
	 * conflict[function][test]
	 */
	protected transient Map<Conflict, Map<String, Integer>> conflicts = new HashMap<Conflict, Map<String, Integer>>();
	protected Map<Conflict, Integer> conflictScores = new HashMap<Conflict, Integer>();
	
	// here Conflict means (caller, callee)
	// transient if NOT CURENTLY IN USE
	protected transient Map<String, Set<Conflict>> totalCallCountDuringTest = new HashMap<String, Set<Conflict>>();
	
	protected SortedSet<String> allTests = new TreeSet<String>();

	private transient String callerSignature; 
	
	@Override
	public double rankFailure(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Integer calls = conflictScores.get(conflict);
		if (calls == null)
			return 0;
		int count = calls;
		
		if (TestRankConstants.USE_ALT_NORMALIZATION) {
			// normalize the conflict count by the total call count of the test
			Integer total = totalCallCountDuringTest.get(testSignature).size();
			double rank = count / ((double)total);
			return rank;
		}
		else {
			return (double)count / (count + 1.0);
		}
	}

	@Override
	public void begin(String methodSignature, String testSignature) {
		allTests.add(testSignature);
		
//		StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
//		StackTraceElement stackTraceElement = StackTraceUtils.getCallingMethod(stackTrace);
		
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Map<String, Integer> calls = conflicts.get(conflict);
		if (calls == null) {
			calls = new HashMap<String, Integer>();
			conflicts.put(conflict, calls);
			conflictScores.put(conflict, 0);
		}
		String callingMethod = callerSignature;
		//String callingMethod = stackTraceElement.toString();//StackTraceUtils.toCanonicalString(stackTraceElement);
		Integer count = calls.get(callingMethod);
		if (count == null) {
			count = 0;
			conflictScores.put(conflict, conflictScores.get(conflict) + 1);
		}
		
		count++;
		calls.put(callingMethod, count);
		
		// update total call set in the test. Conflict is for (caller, callee)
		Set<Conflict> callPairs = totalCallCountDuringTest.get(testSignature);
		if (callPairs == null) {
			callPairs = new HashSet<Conflict>();
			totalCallCountDuringTest.put(testSignature, callPairs);
		}
		callPairs.add(new Conflict(callerSignature, methodSignature));
	}

	@Override
	public void end(String methodSignature, String testSignature) {
		
	}

	@Override
	public String toString() {
		return getName() + ":" + conflicts.toString().replace(", Conflict", ",\nConflict") + "\nTotal Conflicts = " + conflicts.size();
	}
	
	public void printConflicts() {
		System.out.println(toString());
	}

	public Set<Conflict> getAllConflicts() {
		return conflicts.keySet();
	}
	
	@Override
	public String getName() {
		return "CLCNT";
	}

	public void setCallerSignature(String callerSignature) {
		this.callerSignature = callerSignature;
		
	}

}

